﻿module command

open Autodesk.AutoCAD.ApplicationServices.Core
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime
open System
open FsharpCad
open FsharpCad.Library
open IFoxCAD.Cad
type AcColor = Autodesk.AutoCAD.Colors.Color
type AcColorMethod = Autodesk.AutoCAD.Colors.ColorMethod

let Ed = AcCoreApp.DocumentManager.MdiActiveDocument.Editor
let Db = AcCoreApp.DocumentManager.MdiActiveDocument.Database


[<CommandMethod("testDbTrans")>]
let testDbTrans () =
    use tr = new DBTrans()

    let inputs =
        prompt {
            let center = getPointMsg ("\nCenter: ")
            let radius = getDist (center.Value, "\nRadius: ")
            return (center.Value, radius.Value)
        }
    match inputs with
    | None -> ()
    | Some(center, radius) ->
        seq {
            new Circle(center, Vector3d.ZAxis, radius)
            new Circle(center, Vector3d.ZAxis, radius + 500.)
            new Circle(center, Vector3d.ZAxis, radius + 1000.)
        }
        |> addEntities tr.CurrentSpace
        |> ignore

// ===================================== MAIN ====================================== //

// Custom command example
[<CommandMethod("drawGoldenSpiral")>]
let drawGoldenSpiral () =
    let opts = PromptIntegerOptions("\nNumber of loops: ")
    opts.AllowNone <- true
    opts.LowerLimit <- 1
    opts.UpperLimit <- 16
    opts.DefaultValue <- 4
    opts.UseDefaultValue <- true

    let inputs =
        prompt {
            let pir = Ed.GetInteger(opts)
            let ppr = getPointMsg ("\nStart point: ")
            let pdr = getDist (ppr.Value, "\nWidth: ")
            return (ppr.Value, pir.Value * 4 + 1, pdr.Value)
        }

    match inputs with
    | None -> ()
    | Some(pt, n, w) ->
        use tr = Db.TransactionManager.StartTransaction()
        let ratio = sqrt 1.25 - 0.5
        let bulge = - tan(Math.PI / 8.)
        let pline = new Polyline()

        (Point2d(pt.X, pt.Y), (ratio, ratio), w)
        |> Seq.unfold (fun (p, (a, b), d) -> Some(p, (p + Vector2d(d * a, d * b), (b, -a), ratio * d)))
        |> Seq.take n
        |> Seq.iteri (fun i p -> pline.AddVertexAt(i, p, bulge, 0., 0.))

        pline.TransformBy(Ed.CurrentUserCoordinateSystem)

        pline
        |> addEntity (Db.CurrentSpaceId |> getObject<BlockTableRecord> OpenMode.ForWrite)
        |> ignore

        tr.Commit()

[<CommandMethod("DrawCircle")>]
let drawCircle () =
    let inputs =
        prompt {
            let ppr = Ed.GetPoint("\nCenter: ")
            let pdr = getDist (ppr.Value, "\nRadius: ")
            return (ppr.Value, pdr.Value)
        }

    match inputs with
    | None -> ()
    | Some(center, radius) ->
        use tr = Db.TransactionManager.StartTransaction()

        new Circle(center, Vector3d.ZAxis, radius)
        |> addEntity (Db.CurrentSpaceId |> getObject<BlockTableRecord> OpenMode.ForWrite)
        |> ignore

        tr.Commit()

[<CommandMethod("DrawCircle1")>]
let drawCircle1 () =
    let inputs =
        prompt {
            let ppr = getPointMsg ("\nCenter: ")
            let pdr = getDist (ppr.Value, "\nRadius: ")
            return (ppr.Value, pdr.Value)
        }

    use tr = new DBTrans()

    let id =
        match inputs with
        | None -> ObjectId.Null
        | Some(center, radius) -> new Circle(center, Vector3d.ZAxis, radius) |> addEntity tr.CurrentSpace

    let ent = id |> getObject<Line> OpenMode.ForWrite
    ent.ColorIndex <- 3

[<CommandMethod("Drawline")>]
let drawline () =
    use tr = new DBTrans()

    let pt1 = Point3d(0, 0, 0)
    let pt2 = Point3d(10, 10, 0)
    let addtobtr = addEntity tr.CurrentSpace // 函数式编程里的 科里化
    let addentign = addtobtr >> ignore // 函数组合
    new Line(pt1, pt2) |> addEntity tr.CurrentSpace |> ignore
    new Line(pt1, pt2) |> addentign
//addEntity tr.CurrentSpace (new Line(pt1, pt2))






// test add a blockdef to blocktable
[<CommandMethod("addblocktest")>]
let addblocktest () =
    use tr = new DBTrans()

    tr.BlockTable.Add (
        "testblock",
        fun btr ->
            let line = new Line(Point3d(0, 0, 0), Point3d(1, 1, 0))
            line |> addEntity btr |> ignore
    )
    |> ignore


// test add a layer
[<CommandMethod("addlayertest")>]
let addlayertest () =
    use tr = new DBTrans()

    tr.LayerTable.Add (
        "testblock",
        fun ltr ->
            ltr.LineWeight <- LineWeight.LineWeight030
            ltr.Color <- AcColor.FromColorIndex(AcColorMethod.ByColor, 1s)
    )
    |> ignore


[<CommandMethod("ceaddtest")>]
let ceaddtest () =
    use tr = new DBTrans(@"C:\Users\vic\Desktop\test.dwg")
    //add {
    //    btr tr.CurrentSpace

    //    let line = new Line(Point3d(0,0,0), Point3d(1,1,0))
    //    return line
    //} |> ignore

    let line = new Line(Point3d(0, 0, 0), Point3d(1, 1, 0))
    line |> addEntity tr.CurrentSpace |> ignore

    tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current)

[<CommandMethod("loopadd")>]
let loopadd () =
    for i in 1..10 do
        let filepath = $"C:\\Users\\vic\\Desktop\\test{i}.dwg"
        use tr = new DBTrans(filepath)
        let line = new Line(Point3d(0, 0, 0), Point3d(1, 1, 0))
        line |> addEntity tr.CurrentSpace |> ignore

        tr.Database.SaveAs(filepath, DwgVersion.Current)



[<CommandMethod("opprint")>]
let opprint () =

    //``fuck the function`` 1

    let a =
        And[Color 1
            BlockName "1"
            Comp(10, ">", Point3d(1, 1, 0))]
            .build ()
    (*

        -4 and
            62 1 // color
            2 "1" // blockname
            -4 >
            10 point
        -4 and
    *)


    a.ToString() |> print

    let b = ((Color 1) ||| (EntityType "line")) |> ssget "nihc"

    let pt2 = Point3d(1, 1, 0)
    ((Color 1) ||| (EntityType "line")) |> ssgetw (Point3d(0, 0, 0)) pt2 |> ignore

    ((Color 1) ||| (EntityType "line"))
    |> ssgetwp [ Point3d(0, 0, 0); Point3d(0, 1, 0); Point3d(1, 1, 0); Point3d(1, 0, 0) ]
    |> ignore

    b.ToString() |> print



//!+ (Color 1) &+ (LayerName "1") |+ (Color 2) |>  ssget ":A"

//And [And []; Or []; Not (Color 1); Xor ((Color 1), (LayerName "1"))]  |> ignore

//match x with
//| Or(p :: q) -> printfn "%A" p
//| And (p) -> printfn "%A" p
//| _ -> ()


[<CommandMethod("testjig")>]
let testjig () =
    let line = new Line()
    let pt = Point3d.Origin
    let jig = DrawJigEx (pt, [| line |])

    let ents =
        jig
        |> Jig.addkey "打印(S)" (fun x -> print "s") //添加关键字及执行函数
        |> Jig.addkey "dayib(R)" (fun x -> print "r")
        |> Jig.addmessage "移动："
        |> Jig.addoption (fun opt ->
            opt.UseBasePoint <- true
            opt.BasePoint <- pt) // 添加option设置，只支持JigPromptPointOptions
        |> Jig.addaction (fun pt1 -> // 添加动作
            line.EndPoint <- pt1 // 根据当前的点更新外部图元
            jig.DrawGeo (GeoCircle(pt1, 10)) KeepInDatabase // 画临时图元，jig结束后添加到数据库
            jig.DrawGeo (GeoLine(pt1 + Vector3d(-10, -10, 0), pt1)) NotInDatabase // 画临时图元， jig结束后不添加到数据库
        )
        |> Jig.run // 执行jig，结束后返回所有需要添加到数据库的图元

    use tr = new DBTrans()
    ents |> addEntities tr.CurrentSpace |> ignore

type Segment =
    | Segline
    | Segarc

[<CommandMethod("testjigloop")>]
let testjigloop () =
    let line = new Polyline()
    line.SetDatabaseDefaults()
    let pt = Point3d.Origin
    line.AddVertexAt(0, Point2d(pt.X, pt.Y), 0, 0, 0)
    // line.AddVertexAt(1,Point2d(pt.X,pt.Y),0,0,0)
    let mutable seg = Segline
    let jig = DrawJigEx(pt, [| line |])

    let ents =
        jig
        |> Jig.addkey "u" (fun x -> line.RemoveVertexAt(line.NumberOfVertices - 1)) //添加关键字及执行函数
        |> Jig.addkey "r" (fun x -> seg <- Segarc)
        |> Jig.addkey "l" (fun x -> seg <- Segline)
        |> Jig.addmessage "下一点："
        |> Jig.addoption (fun opt ->
            opt.UseBasePoint <- true
            opt.BasePoint <- line.GetPoint3dAt(line.NumberOfVertices - 1)
            opt.UserInputControls <- opt.UserInputControls ||| UserInputControls.AcceptMouseUpAsPoint) // 添加option设置，只支持JigPromptPointOptions
        |> Jig.addaction (fun pt1 -> // 添加动作
            match seg with
            | Segline -> jig.DrawGeo (GeoLine(line.GetPoint3dAt(line.NumberOfVertices - 1), pt1)) NotInDatabase
            | Segarc ->
                let pt = line.GetPoint3dAt(line.NumberOfVertices - 1)

                jig.DrawGeo
                    (GeoArc(pt, Point3d((pt1.X - pt.X) / 2.0, (pt1.Y - pt.Y) / 2.0, (pt1.Z - pt.Z) / 2.0), pt1))
                    NotInDatabase)
        |> Jig.runloop (fun jig ->
            line.AddVertexAt(line.NumberOfVertices, Point2d(jig.LastPoint.X, jig.LastPoint.Y), 0, 0, 0)

            match seg with
            | Segarc -> line.SetBulgeAt(line.NumberOfVertices - 2, 0.2)
            | _ -> ()) // 执行jig，结束后返回所有需要添加到数据库的图元

    use tr = new DBTrans()
    ents |> addEntities tr.CurrentSpace |> ignore


[<CommandMethod("testucs")>]
let testucs () =
    let mdoc = Application.DocumentManager.MdiActiveDocument
    let db = mdoc.Database
    let ed = mdoc.Editor
    let ucs = ed.CurrentUserCoordinateSystem
    let wcs = ucs.Inverse()
    let pntpresult = ed.GetPoint("\nlocation")
    ed.WriteMessage($"\nptucs:{pntpresult.Value}")
    let pt = pntpresult.Value.TransformBy(ucs)
    let wcspr = ucs.PreMultiplyBy(Matrix3d.Displacement(pt.GetAsVector()))
    let pty = pt.TransformBy(wcs)
    let ptyy = pt.TransformBy(wcspr)
    ed.WriteMessage($"\npty:{pty}")
    ed.WriteMessage($"\nptyy:{ptyy}")
    use tr = new DBTrans()
    let cir = new Circle(pt, Vector3d.ZAxis, 10)
    let cir1 = new Circle(ptyy, Vector3d.ZAxis, 10)
    cir |> addEntity tr.CurrentSpace |> ignore
    cir1 |> addEntity tr.CurrentSpace |> ignore

[<CommandMethod("testmat")>]
let testmat () =
    let pt = Point3d(1, 1, 0)
    let mat = [| 1.; 0.; 0.; 1; 0.; 2.; 0; 1; 0.; 0.; 1; 1; 0.; 0.; 0.; 1. |]
    let pt1 = pt.TransformBy(Matrix3d(mat))
    use tr = new DBTrans()
    let pent = new DBPoint(pt1)
    pent |> addEntity tr.CurrentSpace |> ignore

    new DBPoint(pt) |> addEntity tr.CurrentSpace |> ignore




[<CommandMethod("testpt")>]
let testpt () =
    "===== 0.1 =======" |> print
    let pt1 = Point3d(0.1, 0.1, 0)
    let pt2 = Point3d(0.1, 0.1, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.01 =======" |> print
    let pt1 = Point3d(0.01, 0.01, 0)
    let pt2 = Point3d(0.01, 0.01, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.001 =======" |> print
    let pt1 = Point3d(0.001, 0.001, 0)
    let pt2 = Point3d(0.001, 0.001, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.0001 =======" |> print
    let pt1 = Point3d(0.0001, 0.0001, 0)
    let pt2 = Point3d(0.0011, 0.0001, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.00001 =======" |> print
    let pt1 = Point3d(0.00001, 0.00001, 0)
    let pt2 = Point3d(0.00011, 0.00001, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.000001 =======" |> print
    let pt1 = Point3d(0.000001, 0.000001, 0)
    let pt2 = Point3d(0.000011, 0.000001, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.00000001 =======" |> print
    let pt1 = Point3d(0.00000001, 0.00000001, 0)
    let pt2 = Point3d(0.00000011, 0.00000001, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.000000001 =======" |> print
    let pt1 = Point3d(0.000000001, 0.000000001, 0)
    let pt2 = Point3d(0.000000011, 0.000000001, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.0000000001 =======" |> print
    let pt1 = Point3d(0.0000000001, 0.000000001, 0)
    let pt2 = Point3d(0.0000000011, 0.000000001, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.00000000001 =======" |> print
    let pt1 = Point3d(0.00000000001, 0.000000001, 0)
    let pt2 = Point3d(0.00000000011, 0.000000001, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
    "===== 0.000000000001 =======" |> print
    let pt1 = Point3d(0.000000000001, 0.000000001, 0)
    let pt2 = Point3d(0.000000000011, 0.000000001, 0)
    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print

    use tr = new DBTrans()
    "===== 选线 =======" |> print
    let line = Ed.GetEntity("").ObjectId |> getObject<Line> OpenMode.ForRead
    let line2 = Ed.GetEntity("").ObjectId |> getObject<Line> OpenMode.ForRead

    let pt1 = line.StartPoint
    let pt2 = line2.StartPoint

    (pt1 = pt2).ToString() |> print
    pt1.Equals(pt2).ToString() |> print
    pt1.IsEqualTo(pt2).ToString() |> print
